home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 2.toast / pc / sample code / sound / sndplaydoublebuffer / _source / interrupt_routines.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  8.4 KB  |  275 lines

  1. /*
  2.     File:        Interrupt_Routines.c
  3.  
  4.     Contains:    Routines demonstrating how to write interrupt routines to deal with
  5.                 sound and I/O callbacks.
  6.  
  7.     Written by: Mark Cookson    
  8.  
  9.     Copyright:    Copyright © 1996-1999 by Apple Computer, Inc., All Rights Reserved.
  10.  
  11.                 You may incorporate this Apple sample source code into your program(s) without
  12.                 restriction. This Apple sample source code has been provided "AS IS" and the
  13.                 responsibility for its operation is yours. You are not permitted to redistribute
  14.                 this Apple sample source code as "Apple sample source code" after having made
  15.                 changes. If you're going to re-distribute the source, we require that you make
  16.                 it clear in the source that the code was descended from Apple sample source
  17.                 code, but that you've made changes.
  18.  
  19.     Change History (most recent first):
  20.                 8/31/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  21.                 
  22.  
  23. */
  24.  
  25. #include "Interrupt_Routines.h"
  26. #include <Devices.h>
  27.  
  28. /* This gets called when the requested read has completed.
  29.    This is a good place to add any conversion routines to convert
  30.    from your sound format into the format expected by the Sound Manager.
  31.  
  32.    It's important to note that the conversions I am doing here is to help
  33.    show you how to do something more complicated.  The Sound Manager 3.2 has
  34.    an Endian converter ('sowt'), and you can specify if a sound is 'raw '
  35.    or 'twos' to have the Sound Manager do the conversion.  We do the work
  36.    ourselves for entertainment and education.
  37. */
  38. /*-----------------------------------------------------------------------*/
  39. #ifdef powerc
  40. pascal    void    ASoundFileCallBack        (myParmBlkPtr passedPB)
  41. #else
  42. pascal    void    ASoundFileCallBack        (myParmBlkPtr passedPB:__a0)
  43. #endif
  44. /*-----------------------------------------------------------------------*/
  45. {
  46.     long            i            = kInit;
  47.     myParmBlkPtr    myPB        = nil;        /* just in case a0
  48. gets used for something else while we're here */
  49.     OSErr            theErr        = noErr;
  50.     char            dataFormat    = kInit;
  51.  
  52.  
  53.   #ifndef powerc
  54.     long    myA5;
  55.     myA5 = SetA5 (passedPB->myA5);
  56.   #endif
  57.  
  58.     if (passedPB == nil) {
  59.         DebugPrint ("\pmyPB is nil!");
  60.         theErr = kNilPtrErr;
  61.     }
  62.     else {
  63.         myPB = passedPB;
  64.  
  65.         if (myPB->pb.ioParam.ioResult == noErr) {
  66.             if (myPB->theSoundInfo->doubleHeader.dbhSampleSize == kSixteen) {
  67.                 dataFormat += kIs16Bit;
  68.             }
  69.             if (myPB->theSoundInfo->doubleHeader.dbhNumChannels == kStereo) {
  70.                 dataFormat += kIsStereo;
  71.             }
  72.  
  73.             if (myPB->theSoundInfo->needsMasking == true) {
  74.                 for (i = kInit; i <= myPB->pb.ioParam.ioActCount / sizeof (long); i++) {
  75.                     ((long *)myPB->pb.ioParam.ioBuffer)[i] ^= kLongMask;    /* Convert from raw to 2's complement */
  76.                 }
  77.             }
  78.  
  79.             if (myPB->theSoundInfo->fileType == 'WAVE' && myPB->theSoundInfo->doubleHeader.dbhFormat == 'NONE') {    /* We will have to rearange the data so that the Mac can play it properly */
  80.                 switch (dataFormat) {
  81.                     case kMono8Bit:        /* Do nothing (endian only effects words, not bytes) */
  82.                     case kStereo8Bit:
  83.                         break;
  84.                     case kMono16Bit:    /* Do endian conversion (i.e. 0x1234 5678 becomes 0x2143 6587) */
  85.                     case kStereo16Bit:
  86.                             Endian16BitBuffer ((Ptr)myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  87.                         break;
  88.                     default:
  89.                         DebugPrint ("\pBad header passed to ASoundFileCallBack");
  90.                 }
  91.             }
  92.  
  93.             if (ASoundIsBackwards (myPB->theSoundInfo) == true) {    /* reverse the sound in the buffer so it sounds like we are playing backwards */
  94.                 switch (dataFormat) {
  95.                     case kMono8Bit:
  96.                         ReverseMono8BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  97.                         break;
  98.                     case kMono16Bit:
  99.                         ReverseMono16BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  100.                         break;
  101.                     case kStereo8Bit:
  102.                         ReverseStereo8BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  103.                         break;
  104.                     case kStereo16Bit:
  105.                         ReverseStereo16BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  106.                         break;
  107.                     default:
  108.                         DebugPrint ("\pBad header passed to ASoundFileCallBack");
  109.                 }
  110.                 ASoundPlayBackwards (myPB->theSoundInfo, false);
  111.             }
  112.         }
  113.         else {
  114.             DebugPrint("\pError in ASoundFileCallBack, ioResult was not zero!");
  115.             theErr = myPB->pb.ioParam.ioResult;
  116.         }
  117.     }
  118.  
  119.     if (theErr == noErr) {
  120.         myPB->theBuffer->dbFlags |= dbBufferReady;
  121.     }
  122.  
  123.     myPB->pbInUse = false;
  124.  
  125.   #ifndef powerc
  126.     myA5 = SetA5 (myA5);
  127.   #endif
  128.  
  129.     return;
  130. }
  131.  
  132. /* This gets called when the Sound Manager has finished playing the buffer and
  133.    wants you to refill it.
  134.  
  135.    Please note:  It may look like this routine reuses the paramBlock, but
  136. it does
  137.    not, there is a paramBlock for each buffer.  This eliminates race conditions
  138.    while priming the buffer, and makes sure that the paramBlock is not reused.
  139.  
  140.    You CANNOT reuse a paramBlock from within an ioCompletion routine!  This
  141.    will (ok, may) cause terrible problems, especially if you have File Sharing
  142.    turned on.
  143. */
  144. /*-----------------------------------------------------------------------*/
  145. pascal    void    ASoundDoubleBackProc    (SndChannelPtr chan,
  146.  
  147.     SndDoubleBufferPtr doubleBuffer)
  148. /*-----------------------------------------------------------------------*/
  149. {
  150. #ifndef __SC__
  151. #pragma unused (chan)
  152. #endif
  153.  
  154.     SoundInfoPtr    theSoundInfo    = nil;
  155.     myParamBlockRec    *myPB            = nil;
  156.     long            bytesToCopy        = kInit;
  157.     OSErr            theErr            = noErr;
  158.  
  159.   #ifndef powerc
  160.     long    myA5;
  161.   #endif
  162.  
  163.     theSoundInfo = (SoundInfoPtr)doubleBuffer->dbUserInfo[kSndInfoPtr];
  164.     if (IsValid (theSoundInfo) == false) {
  165.         DebugPrint ("\pbad user field, dbUserInfo[0] is probably nil!");
  166.         theErr = kNilPtrErr;
  167.     }
  168.     else {
  169.         myPB = (myParmBlkPtr)doubleBuffer->dbUserInfo[kPBPtr];
  170.         if (myPB == nil) {
  171.             DebugPrint ("\pbad user field, myPB is nil!");
  172.             theErr = kNilPtrErr;
  173.         }
  174.     }
  175.  
  176.   #ifndef powerc
  177.     myA5 = SetA5 (myPB->myA5);
  178.   #endif
  179.  
  180.     /* The pbInUse field was added because of a race condition caused by the fact that
  181.        the Sound Manager calls this routine one last time after the sound has stopped,
  182.        and in some cases when the sound has stopped we want to read from the start of
  183.        the file right away.  This seems to fix the problem of file errors when stopping
  184.        and starting a sound rapidly.
  185.  
  186.        If the PB is in use we just don't do the requested read.
  187.        The pbInUse flag is cleared at the end of ASoundFileCallBack. */
  188.     if (theErr == noErr && myPB->pbInUse == false && theSoundInfo->stopping == false) {
  189.         myPB->pb.ioParam.ioBuffer = (Ptr)doubleBuffer->dbSoundData;
  190.  
  191.         bytesToCopy = ASoundGetNumTotalBytes(theSoundInfo) - ASoundGetBytesCopied (theSoundInfo);
  192.  
  193.         if (bytesToCopy > ASoundGetBufferSize (theSoundInfo)) {
  194.             bytesToCopy = ASoundGetBufferSize (theSoundInfo);
  195.         }
  196.  
  197.         myPB->pb.ioParam.ioReqCount = bytesToCopy;
  198.         myPB->pb.ioParam.ioPosOffset = ASoundGetBytesCopied (theSoundInfo);
  199.  
  200.         if (myPB->pb.ioParam.ioPosOffset < theSoundInfo->dataStart)    {    /* A little extra sanity checking */
  201.             myPB->pb.ioParam.ioPosOffset = theSoundInfo->dataStart;
  202.         }
  203.  
  204.         myPB->theBuffer = doubleBuffer;                /* Which buffer are we filling? */
  205.         if (bytesToCopy > kInit) {                    /* Do we really need to read more sound?*/
  206.             myPB->pbInUse = true;
  207.             theErr = PBReadAsync(&myPB->pb);        /* Do an async read of more sound */
  208.         }
  209.  
  210.         if (theErr >= noErr) {
  211.             theErr = noErr;                            /* positive errors are not real errors */
  212.             theSoundInfo->bytesCopied += bytesToCopy;
  213.             if (theSoundInfo->bytesPerFrame > kInit) {
  214.                 doubleBuffer->dbNumFrames = bytesToCopy / theSoundInfo->bytesPerFrame;
  215.             }
  216.             else {
  217.                 DebugPrint ("\pbytesPerFrame is a bad value!");
  218.             }
  219.  
  220.             if (theSoundInfo->bytesCopied == theSoundInfo->bytesTotal)
  221.                 doubleBuffer->dbFlags |= dbLastBuffer;
  222.  
  223.             theSoundInfo->currentBuffer++;
  224.         }
  225.         else {
  226.             DebugPrint ("\pPBReadAsync error!");
  227.         }
  228.     }
  229.  
  230.   #ifndef powerc
  231.     (void)SetA5 (myA5);
  232.   #endif
  233.  
  234.     return;
  235. }
  236.  
  237. /* This gets called when the sound is finally done playing.
  238.    It sets a flag that we check in our event loop because you can't clean up
  239.    everything at interrupt time.
  240. */
  241. /*-----------------------------------------------------------------------*/
  242. pascal    void    ASoundDoneCallBack        (SndChannelPtr chan,
  243.  
  244.     SndCommand *cmd)
  245. /*-----------------------------------------------------------------------*/
  246. {
  247. #ifndef __SC__
  248. #ifdef powerc
  249. #pragma unused (cmd)
  250. #endif
  251. #endif
  252.  
  253.     SoundInfoPtr        theSoundInfo = nil;
  254.  
  255.   #ifndef powerc
  256.     long    myA5;
  257.     myA5 = SetA5 (cmd->param2);
  258.   #endif
  259.  
  260.     theSoundInfo = (SoundInfoPtr)chan->userInfo;
  261.     if (StrictIsValid (theSoundInfo)) {
  262.         theSoundInfo->soundDone = true;
  263.     }
  264.     else {
  265.         DebugPrint ("\pbad sound channel pointer passed to ASoundDoneCallBack");
  266.     }
  267.  
  268.   #ifndef powerc
  269.     myA5 = SetA5 (myA5);
  270.   #endif
  271.  
  272.     return;
  273. }
  274.  
  275.